from ast import Dict
import consul
from .single import SingletonMeta
import settings
import uuid
import socket
from loguru import logger
from dns import asyncresolver,rdatatype
from typing import List, Dict, Tuple, Optional

def get_current_ip() -> str:
    sock_ip=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock_ip.connect(("8.8.8.8", 80))
    ip = sock_ip.getsockname()[0]
    sock_ip.close()
    return ip

class ServiceAddress:
    def __init__(self,host:str,port:int) -> None:
        self.host=host
        self.port=port
        self.count=0
    def increment_count(self):
        self.count+=1
    

class LoadBalancer():
    def __init__(self,addresses:List[Dict[str,str|int]]|None=None) -> None:
        self.addresses=[]
        if addresses:
            self.init_addresses(addresses)

    def init_addresses(self,addresses:List[Dict[str,str|int]]):
        self.addresses.clear()
        for address in addresses:
            host = address['host']
            port = address['port']
            if isinstance(host, str) and isinstance(port, int):
                self.addresses.append(ServiceAddress(host=host,port=port))
    
    def get_address(self)->Tuple[str|None, int|None]:
        if not self.addresses or len(self.addresses)==0:
            return None,None
        self.addresses.sort(key=lambda x:x.count)
        address=self.addresses[0]
        address.increment_count()
        return address.host,address.port


class ConsulClient(metaclass=SingletonMeta):
    def __init__(self) -> None:
        self.consul_host=settings.CONSUL_HOST
        self.consul_http_port=settings.CONSUL_HTTP_PORT
        self.consul_dns_port=settings.CONSUL_DNS_PORT
        self.client=consul.Consul(host=self.consul_host,port=self.consul_http_port)
        self.user_server_id=uuid.uuid4().hex
        self.user_service_lb=LoadBalancer(addresses=None)
        
    def register_service(self,service_name:str)->None:
        ip=get_current_ip()
        port=settings.SERVER_PORT
        self.client.agent.service.register(
            name=service_name,
            service_id=self.user_server_id,
            address=ip,
            port=port,
            tags=['user','http'],
            check=consul.Check().http(f'http://{ip}:{port}/health', '30s', deregister='5m'),
        )
        logger.info(f"id：{self.user_server_id}，{service_name}已 注册成功")
    
    def deregister_service(self):
        if self.user_server_id:
            self.client.agent.service.deregister(self.user_server_id)
            self.user_server_id=None
            logger.info(f"服务{self.user_server_id}已 注销成功")
            
    async def fetch_user_service_addresses(self, only_healthy: bool = True)->List[str]:
        """获取用户服务地址列表
        
        Args:
            only_healthy: 是否只返回健康的服务实例
        """
        # 先清理一次critical状态的服务
        try:
            cleaned = self.cleanup_critical_services(service_name="user_service")
            if cleaned > 0:
                logger.info(f"清理了 {cleaned} 个critical状态的user_service实例")
        except Exception as e:
            logger.warning(f"清理critical服务失败: {e}")
        
        if only_healthy:
            # 使用Consul的健康检查API获取健康的服务实例
            try:
                health_services = self.client.health.service("user_service", passing=True)
                user_addresses = []
                for service_info in health_services[1]:
                    service = service_info['Service']
                    address = service['Address']
                    port = service['Port']
                    user_addresses.append({"host": address, 'port': port})
                
                self.user_service_lb.init_addresses(user_addresses)
                logger.info(f"发现 {len(user_addresses)} 个健康的user_service实例")
                return [f"{addr['host']}:{addr['port']}" for addr in user_addresses]
                
            except Exception as e:
                logger.error(f"通过健康检查获取服务地址失败: {e}")
                # 如果健康检查失败，回退到DNS查询
        
        # DNS查询方式（原有逻辑）
        resolver = asyncresolver.Resolver()
        resolver.nameservers = [self.consul_host]
        resolver.port = self.consul_dns_port
        try:
            answer_ip = await resolver.resolve("user_service.service.consul", rdatatype.A)
            ips=[ip.to_text() for ip in answer_ip]
            answer_ports = await resolver.resolve("user_service.service.consul", rdatatype.SRV)
            ports=[]
            for srv_record in answer_ports:
                # SRV记录格式: priority weight port target
                srv_text = srv_record.to_text()
                srv_parts = srv_text.split()
                if len(srv_parts) >= 3:
                    ports.append(int(srv_parts[2]))
            user_addresses = []
            for index, port in enumerate(ports):
                if len(ips) == 1:
                    user_addresses.append({"host": ips[0], 'port': port})
                else:
                    user_addresses.append({"host": ips[index], 'port': port})
            self.user_service_lb.init_addresses(user_addresses)
            return [f"{addr['host']}:{addr['port']}" for addr in user_addresses]
        except Exception as e:
            logger.error(f"获取服务地址失败: {e}")
            return [] 
            
    def get_one_user_service_address(self)->Tuple[str|None, int|None]:
        return self.user_service_lb.get_address()
    
    def get_balanced_address(self) -> str:
        """获取负载均衡的服务地址，返回字符串格式"""
        host, port = self.get_one_user_service_address()
        if host is None or port is None:
            raise RuntimeError("无法获取可用的服务地址")
        return f"{host}:{port}"
    
    def get_all_services(self, service_name: Optional[str] = None) -> Dict:
        """获取所有服务实例"""
        try:
            if service_name:
                # 获取指定服务的所有实例
                services = self.client.health.service(service_name, passing=False)
                return services[1]  # 返回服务列表
            else:
                # 获取所有服务
                services = self.client.agent.services()
                return services
        except Exception as e:
            logger.error(f"获取服务列表失败: {e}")
            return {}
    
    def get_critical_services(self, service_name: Optional[str] = None) -> List[Dict]:
        """获取所有处于critical状态的服务实例"""
        try:
            critical_services = []
            if service_name:
                # 检查指定服务的健康状态
                health_checks = self.client.health.service(service_name, passing=False)
                for service_info in health_checks[1]:
                    checks = service_info.get('Checks', [])
                    for check in checks:
                        if check.get('Status') == 'critical':
                            critical_services.append({
                                'service_id': service_info['Service']['ID'],
                                'service_name': service_info['Service']['Service'],
                                'address': service_info['Service']['Address'],
                                'port': service_info['Service']['Port'],
                                'check_id': check['CheckID'],
                                'status': check['Status'],
                                'output': check.get('Output', '')
                            })
            else:
                # 获取所有critical状态的检查
                health_checks = self.client.health.state('critical')
                for check in health_checks:
                    if isinstance(check, dict) and check.get('ServiceID'):  # 只处理服务相关的检查
                        critical_services.append({
                            'service_id': check['ServiceID'],
                            'service_name': check.get('ServiceName', 'Unknown'),
                            'check_id': check['CheckID'],
                            'status': check['Status'],
                            'output': check.get('Output', '')
                        })
            return critical_services
        except Exception as e:
            logger.error(f"获取critical服务失败: {e}")
            return []
    
    def cleanup_critical_services(self, service_name: Optional[str] = None, max_age_minutes: int = 10) -> int:
        """清理处于critical状态超过指定时间的服务实例
        
        Args:
            service_name: 指定要清理的服务名，None表示清理所有服务
            max_age_minutes: critical状态持续时间阈值（分钟），超过此时间将被清理
            
        Returns:
            清理的服务实例数量
        """
        import time
        cleaned_count = 0
        
        try:
            critical_services = self.get_critical_services(service_name)
            current_time = time.time()
            
            for service in critical_services:
                service_id = service.get('service_id')
                if not service_id:
                    continue
                    
                # 获取服务的详细信息
                try:
                    # 注意：consul库可能没有直接的service.get方法，我们直接清理critical状态的服务
                    # 在生产环境中，你可能需要更精确的时间检查
                    logger.warning(f"准备清理critical服务: {service['service_name']} (ID: {service_id})")
                    
                    # 注销服务
                    self.client.agent.service.deregister(service_id)
                    logger.info(f"已清理critical服务: {service['service_name']} (ID: {service_id})")
                    cleaned_count += 1
                    
                except Exception as e:
                    logger.error(f"清理服务 {service_id} 失败: {e}")
                    
        except Exception as e:
            logger.error(f"清理critical服务失败: {e}")

        return cleaned_count
    
    def force_cleanup_service_instances(self, service_name: str):
        """强制清理指定服务的所有实例（危险操作，谨慎使用）"""
        cleaned_count = 0
        try:
            services = self.get_all_services(service_name)
            for service_info in services:
                service_id = service_info['Service']['ID']
                logger.warning(f"强制清理服务实例: {service_name} (ID: {service_id})")
                self.client.agent.service.deregister(service_id)
                cleaned_count += 1
                logger.info(f"已强制清理服务实例: {service_name} (ID: {service_id})")
        except Exception as e:
            logger.error(f"强制清理服务实例失败: {e}")